/*
 ============================================================================
 Name        : GingerBreadMan.c
 Author      : Jorge García
 Version     : 1.0
 Copyright   : Bajo los terminos de la licencia GNU GPL V 3 y posteriores
 Description : Fractal del hombre de gengibre en OpenGL y glut
 ============================================================================
 */
#include <GL/glut.h>
#include <math.h>
#include <stdlib.h>

#define ITERACIONES 1000
#define numFractals 50

typedef struct {
	float x, y;
	float color[3];
}Punto;

void inicializa(void);
void dibuja(void);
void gingerbreadman(Punto, float M, float L);
void raton(int boton, int estado, int mx, int my);
void teclado(unsigned char key, int mx, int my);

/* Arreglo para guardar los puntos iniciales de cada fractal */
Punto p0[numFractals];
/* Numero de puntoos iniciales, por ende numero de fractales a dibujar */
int numPuntos;
/* Para tomar las coordenadas del mouse, en glut */
int alto_ventana;
float color[3];

int main(int argc, char* argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
    glutInitWindowSize(640,480);
    glutInitWindowPosition(100, 150);
    glutCreateWindow("Fractal del hombre de Gengibre");
    inicializa();

    glutDisplayFunc(dibuja);
    glutMouseFunc(raton);
    glutKeyboardFunc(teclado);
    glutMainLoop();

    return 0;
}

void inicializa() {
    glClearColor(1.0, 1.0, 1.0, 1.0);
    /*Tamaño de puntos y el antialias a todo lo que da*/
    glPointSize(4.0);
    glEnable(GL_POINT_SMOOTH);
    glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	
	/* No hay puntos y el color por defecto es rojo */
    numPuntos = 0;
    color[0] = 1.0;
    color[1] = 0.0;
    color[2] = 0.0;

	/* Poner el sistema de refrencia */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 640.0, 0.0, 480.0);
    alto_ventana = 480;
}

void dibuja(void) {
	int i;

	/* Limpiar la pantalla */
    glClear(GL_COLOR_BUFFER_BIT);
    
	/* Dibujar un fractal por cada punto inicial */
    for (i = 0; i < numPuntos && numPuntos > 0; i++) {
    	glColor3fv(p0[i].color); /* Cada fractal de un color especifico */
    	gingerbreadman(p0[i], 40.0, 3.0);
    }


    glFlush();
}

void raton(int boton, int estado, int mx, int my) {

	if (boton == GLUT_LEFT_BUTTON && estado == GLUT_DOWN) {
		/* Si el numero de puntos iniciales es menor que le maximo numero
		 * permitido de fractales, agrega uno mas */
		if (numPuntos < numFractals) { 
			/* Guarda el punto inicial */
			p0[numPuntos].x = mx;
			p0[numPuntos].y = alto_ventana - my;
			/* Y el color actual */
			p0[numPuntos].color[0] = color[0];
			p0[numPuntos].color[1] = color[1];
			p0[numPuntos].color[2] = color[2];

			numPuntos++;
		}

	}

	glutPostRedisplay();
	return;
}

/* Dado un punto inicial y los parametros de forma M y L, dibuja el 
 * fractal del hombre de jengibre correspondiente */
void gingerbreadman(Punto p0, float M, float L) {
	int i;
	Punto p = p0;
	float x, y;

	glBegin(GL_POINTS);
		for (i = 0; i< ITERACIONES; i++) {
			glVertex2f(p.x, p.y);
			x = M * (1.0 + 2.0 * L) - p.y + fabs(p.x - L * M);
			y = p.x;
			p.x = x;
			p.y = y;
		}
	glEnd();
}

/* Con el teclado cambia el color actual */
void teclado(unsigned char key, int mx, int my) {
	
	switch (key) {
		/* Rojo */
		case 'r':
		case 'R':
			color[0] = 1.0;
			color[1] = 0.0;
			color[2] = 0.0;
		break;
		/* Verde */
		case 'g':
		case 'G':
			color[0] = 0.0;
			color[1] = 1.0;
			color[2] = 0.0;
		break;
		/* Azul */
		case 'b':
		case 'B':
			color[0] = 0.0;
			color[1] = 0.0;
			color[2] = 1.0;
		break;
		/* Amarillo */
		case 'y':
		case 'Y':
			color[0] = 1.0;
			color[1] = 1.0;
			color[2] = 0.0;
		break;
		/* Con la tecla esc termina sin errores */
		case 27:
			exit(0);
		break;
	}

	glutPostRedisplay();
}
